﻿using SharpDX.Direct3D;
using SharpDX.Direct3D9;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows;
using D2D = SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Mathematics.Interop;
using DXGI = SharpDX.DXGI;
using D3D11 = SharpDX.Direct3D11;
using D3D9 = SharpDX.Direct3D9;
using Dw = System.Drawing;
using Agora.Rtc;
using SharpDX;
using SharpDX.Direct2D1;
using System.Data;
using SharpDX.Direct3D11;
using MyMeeting.CustomRender;
namespace MyMeeting.src.Advanced.CustomRender
{
   public  class SharpDx11Render:BaseRender
    {
        public System.Windows.Interop.D3DImage _d3D;
        private bool disposing_;
        private float _x;
        private float _y;
        private float _dx = 1;
        private float _dy = 1;
        private D3D11.Device device;
        private D3D9.Texture _renderTarget;
        private Dw.Size size_ = new Dw.Size();
        private Size  backSurfaceBufferSize= new Size();//backSurfaceBufferSize
        private Size  offScrrenBufferSize= new Size();//offScrrenBufferSize

        private D2D.RenderTarget _d2DRenderTarget;
        private D3D9.Surface _offScreenSurface;//_offScreenSurface

        private DataType data_type_ = DataType.kYUV420;

        private Texture2D _texture;
        private RenderTargetView _renderTargetView;
        private D3D9.Texture _sharedTexture;
        public SharpDx11Render(System.Windows.Interop.D3DImage img,Dw.Size size)
        {
            _d3D = img;
            CreateAndBindTargets(size.Width ,size.Height);
        }


        public  void Deliver()
        {
            _d2DRenderTarget.BeginDraw();

            OnRender(_d2DRenderTarget);

            _d2DRenderTarget.EndDraw();

            _d3D.Lock();

            _d3D.AddDirtyRect(new Int32Rect(0, 0, _d3D.PixelWidth, _d3D.PixelHeight));

            _d3D.Unlock();


        }

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            _d2DRenderTarget.BeginDraw();

            OnRender(_d2DRenderTarget);

            _d2DRenderTarget.EndDraw();

            _d3D.Lock();

            _d3D.AddDirtyRect(new Int32Rect(0, 0, _d3D.PixelWidth, _d3D.PixelHeight));

            _d3D.Unlock();


        }

        private void OnRender(D2D.RenderTarget renderTarget)
        {
            var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new RawColor4(255, 0, 0, 1));

            // renderTarget.Clear(null);

            //renderTarget.DrawRectangle(new RawRectangleF(_x, _y, _x + 10, _y + 10), brush);

            //_x = _x + _dx;
            //_y = _y + _dy;
            //if (_x >= ActualWidth - 10 || _x <= 0)
            //{
            //    _dx = -_dx;
            //}

            //if (_y >= ActualHeight - 10 || _y <= 0)
            //{
            //    _dy = -_dy;
            //}
        }

      

        private void CreateAndBindTargets(int width,int height)
        {
            //var width = Math.Max((int)ActualWidth, 100);
            //var height = Math.Max((int)ActualHeight, 100);

            var renderDesc = new D3D11.Texture2DDescription
            {
                BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource,
                Format = DXGI.Format.B8G8R8A8_UNorm,
                Width = width,
                Height = height,
                MipLevels = 1,
                SampleDescription = new DXGI.SampleDescription(1, 0),
                Usage = D3D11.ResourceUsage.Default,
                OptionFlags = D3D11.ResourceOptionFlags.Shared,
                CpuAccessFlags = D3D11.CpuAccessFlags.None,
                ArraySize = 1
            };

             device = new D3D11.Device(DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport);
          

            var renderTarget = new D3D11.Texture2D(device, renderDesc);

            // 创建渲染目标视图
         

            var surface = renderTarget.QueryInterface<DXGI.Surface>();

            var d2DFactory = new D2D.Factory();

            var renderTargetProperties =
                new D2D.RenderTargetProperties(new D2D.PixelFormat(DXGI.Format.Unknown, D2D.AlphaMode.Premultiplied));

            _d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);

            SetRenderTarget(renderTarget);
            _renderTargetView = new RenderTargetView(device, _texture);
            device.ImmediateContext.Rasterizer.SetViewport(0, 0, size_.Width, size_.Height);
          
        }

        private void SetRenderTarget(D3D11.Texture2D target)
        {
            var format = TranslateFormat(target);
            var handle = GetSharedHandle(target);

            var presentParams = GetPresentParameters();
            var createFlags = D3D9.CreateFlags.HardwareVertexProcessing | D3D9.CreateFlags.Multithreaded |
            D3D9.CreateFlags.FpuPreserve;

            var d3DContext = new D3D9.Direct3DEx();
            var d3DDevice = new D3D9.DeviceEx(d3DContext, 0, D3D9.DeviceType.Hardware, IntPtr.Zero, createFlags,
            presentParams);

            _renderTarget = new D3D9.Texture(d3DDevice, target.Description.Width, target.Description.Height, 1,
                D3D9.Usage.RenderTarget, format, D3D9.Pool.Default, ref handle);
            _texture = target;

            _d3D.Dispatcher.Invoke(() =>
            {
                // 将共享纹理绑定到 D3DImage
                using (var surface = _renderTarget.GetSurfaceLevel(0))
                {
                    _d3D.Lock();
                    _d3D.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface.NativePointer);
                    _d3D.Unlock();
                }
            });

        }

        private static D3D9.PresentParameters GetPresentParameters()
        {
            var presentParams = new D3D9.PresentParameters();

            presentParams.Windowed = true;
            presentParams.SwapEffect = D3D9.SwapEffect.Discard;
            presentParams.DeviceWindowHandle = NativeMethods.GetDesktopWindow();
            presentParams.PresentationInterval = D3D9.PresentInterval.Default;

            return presentParams;
        }

        private IntPtr GetSharedHandle(D3D11.Texture2D texture)
        {
            using (var resource = texture.QueryInterface<DXGI.Resource>())
            {
                return resource.SharedHandle;
            }
        }

        private static D3D9.Format TranslateFormat(D3D11.Texture2D texture)
        {
            switch (texture.Description.Format)
            {
                case SharpDX.DXGI.Format.R10G10B10A2_UNorm:
                    return D3D9.Format.A2B10G10R10;
                case SharpDX.DXGI.Format.R16G16B16A16_Float:
                    return D3D9.Format.A16B16G16R16F;
                case SharpDX.DXGI.Format.B8G8R8A8_UNorm:
                    return D3D9.Format.A8R8G8B8;
                default:
                    return D3D9.Format.Unknown;
            }
        }

        public override void Dispose()
        {
            if (disposing_ == true)
                return;

            disposing_ = true;

            StopFpsTimer();


            if (_offScreenSurface != null)
                _offScreenSurface.Dispose();
            
            if(_sharedTexture!=null)
            {
                _sharedTexture.Dispose();
            }
            if(_renderTargetView!=null)
            {
                _renderTargetView.Dispose();
            }
            if (_texture != null)
            {
                _texture.Dispose();
            }

            if (device != null)
                device.Dispose();

            
        }
        public override void Clear()
        {
            throw new NotImplementedException();
        }

        public override CustomVideoBoxRenderType GetRenderType()
        {
            return data_type_ == DataType.kBGRA ? CustomVideoBoxRenderType.kSharpDX_BGRA : CustomVideoBoxRenderType.kSharpDX_YUV420;
        }

        public override bool Initialize(IntPtr handle, Dw.Size size, DataType dataType)
        {
            size_ = size;
            data_type_ = dataType;
            return true;
        }

        public override void UpdateSize(Dw.Size size)
        {
            size_ = size;
        }

        public override void SetD3dImag(D3DImage img)
        {
            
        }

        private bool AutoCreateD3DDevice(int frame_width, int frame_height, int wnd_width, int wnd_height)
        {
           // _offScreenSurface = _renderTarget.GetSurfaceLevel(0);

            offScrrenBufferSize.Width = frame_width;
            offScrrenBufferSize.Height = frame_height;

            backSurfaceBufferSize.Width = wnd_width;
            backSurfaceBufferSize.Height = wnd_height;

            return true;
        }

       

        public override void DeliverFrame(VideoFrame videoFrame)
        {
            if (disposing_) return;

            if (data_type_ == DataType.kBGRA && videoFrame.type != VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_BGRA && videoFrame.type != VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_RGBA)
                return;

            if (data_type_ == DataType.kYUV420 && videoFrame.type != VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_I420)
                return;

            if (AutoCreateD3DDevice(videoFrame.width, videoFrame.height, (int)size_.Width, (int)size_.Height) != true)
            {
                Console.WriteLine("d3d failed to create d3d device when deliver frame");
                return;
            }

          
            var dataBox = new DataBox(videoFrame.yBufferPtr, videoFrame.width*4,0);

            device.ImmediateContext.UpdateSubresource(dataBox, _texture, 0);

            // 渲染到屏幕
           // device.ImmediateContext.OutputMerger.SetRenderTargets(_renderTargetView);
            //device.ImmediateContext.ClearRenderTargetView(_renderTargetView, new RawColor4(0, 0, 0, 1));

            _d3D.Dispatcher.Invoke(new Action(() =>
            {
                _d3D.Lock();

                _d3D.AddDirtyRect(new Int32Rect(0, 0, _d3D.PixelWidth, _d3D.PixelHeight));

                _d3D.Unlock();
            }));
            
            // present
            //  device.Present();

           // FeedOneFrame();

        }

        public override bool NeedInvoke()
        {
            return false;
        }
    }

public static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = false)]
    public static extern IntPtr GetDesktopWindow();
}
}
